<template>
  <view>
    <view class="album">
      <view
        class="album-item"
        v-for="item in 18"
        :key="item"
        @touchstart="onStart($event, item)"
        @touchmove.prevent="onMove($event, item)"
        @touchend="onEnd"
      >
        <image src="/static/logo.png" class="album-item-img" :class="{ selected: selectedItems.includes(item) }" :draggable="false" :data-id="item" />
      </view>
    </view>

    <view class="remark">
      <view>实现思路：</view>
      <view>
        多选相册基于 document 对象的 elementFromPoint 方法实现，该方法可以获取指定位置的元素，利用该特性可以获取手指滑动时目标元素的相关信息，比如 ID
        或者索引，然后就可以选择从首个选择的元素到该元素的所有元素。
      </view>
      <view>不足之处：</view>
      <view>
        由于时间关系，此 Demo 仅实现了基本的滑动多选和单选功能，还有很多需要改进的地方，比如滑动多选目前还不是很精确，还有再次滑动的取消选中的功能。
      </view>
    </view>
  </view>
</template>

<script>
export default {
  data() {
    return {
      isSelecting: false,
      selectedItems: [],
      startX: 0,
      startY: 0,
      startItem: 0,
    }
  },

  onLoad() {},

  methods: {
    toggleSelection(item) {
      // 切换相册项的选中状态
      if (this.selectedItems.includes(item)) {
        // 已经选中，取消选中
        this.selectedItems = this.selectedItems.filter(selected => selected !== item)
      } else {
        // 未选中，添加到选中项
        this.selectedItems.push(item)
      }
    },

    onStart(event, item) {
      // 开始多选，记录起始位置
      this.isSelecting = true
      this.startX = event.touches[0].clientX
      this.startY = event.touches[0].clientY

      // 切换相册项的选中状态
      this.toggleSelection(item)

      this.startItem = +item
    },

    onMove(event) {
      if (!this.isSelecting) return

      const currentX = event.touches[0].clientX
      const currentY = event.touches[0].clientY

      // 计算滑动距离
      const deltaX = currentX - this.startX
      const deltaY = currentY - this.startY

      // 根据滑动方向判断是否选中相册项
      if (Math.abs(deltaX) > Math.abs(deltaY)) {
        // 水平滑动
        if (deltaX > 0) {
          // 从左向右滑动，选中相册项
          // this.toggleSelection(item)
          const element = document.elementFromPoint(currentX, currentY)

          if (element && element.parentElement && element.parentElement.dataset.id) {
            const item = element.parentElement.dataset.id

            for (let i = this.getMin(+this.startItem, +item); i <= this.getMax(this.startItem, +item); i++) {
              if (this.selectedItems.includes(i)) {
                // do something
              } else {
                this.selectedItems.push(i)
              }
            }
          }
        }

        // 更新起始位置
        this.startX = currentX
        this.startY = currentY
      }
    },

    onEnd() {
      // 结束多选
      this.isSelecting = false
    },

    getMax(a, b) {
      return a >= b ? a : b
    },

    getMin(a, b) {
      return a <= b ? a : b
    },
  },
}
</script>

<style lang="scss">
.album {
  width: 100%;
  max-width: 500px;
  margin: 0 auto;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  padding: 20rpx;
  box-sizing: border-box;

  gap: 10rpx;

  &-item {
    overflow: hidden;
    aspect-ratio: 1/1;

    &-img {
      width: 100%;
      height: 100%;
      display: block;
    }
  }

  .selected {
    &::after {
      content: "";
      position: absolute;
      right: 10rpx;
      bottom: 10rpx;
      width: 30rpx;
      height: 30rpx;
      border-radius: 50%;
      background-color: blue;
      border: 5rpx solid white;
    }
  }
}

.remark {
  padding: 20rpx;

  display: grid;
  gap: 20rpx;
}
</style>
